package com.hockeo.client.ui.base;

import java.util.List;

import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.RowNumberer;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.hockeo.client.model.JSData;
import com.hockeo.shared.rpc.classic.IRpcResultReceiver;

/**
 * Base class for all VH specific grids.
 *
 * @version $Id$
 * @author jjanke
 */
public abstract class VHGrid extends LayoutContainer implements IRpcResultReceiver<List<JSData>>
{
  private final Grid<JSData>      d_grid;
  private final ListStore<JSData> d_store;
  private final Object[]          d_aobjConfig;
  private final VHGridContextMenu d_ctxMenu;
  private RowNumberer             d_rowNumberer;
  private ColumnConfig            d_colcfgAutoExpand;

  /**
   * Creates a new VHGrid without specific configuration.
   */
  protected VHGrid()
  {
    this( null );
  }

  /**
   * Creates a new VHGrid.
   *
   * @param aobjConfig the configuration parameters that are accepted (may be used by
   *          subclasses)
   */
  protected VHGrid( Object[] aobjConfig )
  {
    d_aobjConfig = aobjConfig;

    if ( d_aobjConfig != null && d_aobjConfig.length > 0 )
      applyConfiguration( aobjConfig );

    setLayout( new FitLayout() );

    d_store = createStore();
    d_grid = createGrid();
    d_ctxMenu = createContextMenu();

    add( d_grid );
  }

  /**
   * This method is called with all the configuration parameters that have been passed to
   * the constructor. It should be overwritten by sub-classes that want to evaluate these
   * parameters in a specific way.
   *
   * @param aobjConfig the configuration parameters
   */
  protected void applyConfiguration( Object[] aobjConfig )
  {
  // empty default implementation
  }

  /**
   * Loads the data that should be displayed by the grid. By default, the methods
   * implementation is empty. The method should be overridden by subclasses.
   *
   * @param aobjConfig all configuration parameters (actual arguments depend on the
   *          subclass' implementation)
   */
  public void loadData( Object... aobjConfig )
  {
  // empty default implementation
  }

  /**
   * Creates a new store and returns it. By default, an empty {@link ListStore} is
   * created. If you need another store (e.g. a GroupingStore) or want to specifically
   * customise the created store, override this method!
   */
  protected ListStore<JSData> createStore()
  {
    return new ListStore<JSData>();
  }

  /**
   * Creates a new Grid and returns it. This method calls {@link #customiseGrid(Grid)} to
   * allow customising the newly created grid without having to override the actual grid
   * creation.
   */
  protected Grid<JSData> createGrid()
  {
    Grid<JSData> grid = new Grid<JSData>( d_store, createColumnModel() );

    customiseGrid( grid );

    return grid;
  }

  /**
   * Creates the grid's context menu. By default no menu is created (i.e. this method
   * returns <code>null</code>). Override it to create a customised context menu.
   */
  protected VHGridContextMenu createContextMenu()
  {
    return null;
  }

  /**
   * Customises a newly created grid. Override this method for specific cusomisation but
   * do not forget to call to the super method <code>super.customiseGrid(grid);</code> in
   * order to make sure that standard cusomisation is carried out.
   *
   * @param grid the grid to be customised
   */
  protected void customiseGrid( Grid<JSData> grid )
  {
    grid.setBorders( true );
    grid.setAutoWidth( true );
    grid.setStripeRows( true );

    if ( d_colcfgAutoExpand != null )
      grid.setAutoExpandColumn( d_colcfgAutoExpand.getId() );

    if ( d_rowNumberer != null )
      grid.addPlugin( d_rowNumberer );
  }

  /**
   * Creates the column model for this grid.
   */
  protected abstract ColumnModel createColumnModel();

  /**
   * Returns the row numberer for this grid. If one is created by calling this method at
   * least once it is also added to the grid as plugin during the execution of the
   * {@link #customiseGrid(Grid)} method.
   */
  protected RowNumberer getRowNumberer()
  {
    if ( d_rowNumberer == null )
      d_rowNumberer = new RowNumberer();

    return d_rowNumberer;
  }

  /**
   * Sets the column that should be expanded automatically.
   *
   * @param colcfg the column to be auto-expanded, the last set column prevails
   */
  protected void setAutoExpandColumn( ColumnConfig colcfg )
  {
    d_colcfgAutoExpand = colcfg;
  }

  /**
   * Returns the actual low level grid widget.
   */
  public Grid<JSData> getLowLevelGrid()
  {
    return d_grid;
  }

  /**
   * Returns the underlying data store.
   */
  public ListStore<JSData> getStore()
  {
    return d_store;
  }

  /**
   * This method is called when the grid has been configured as the result receiver of an
   * RPC call and the RPC call successfully returns some data. The returned data is used
   * as content of the grid's store. I.e. the previous content of the store will be
   * replaced. Override this method if you need special handling.
   */
  @Override
  public void processRpcResult( List<JSData> listResult, String strPurpose )
  {
    d_store.removeAll();
    d_store.add( listResult );
  }
}
